//strips non basic-alphanumeric chars (including spaces)
function descrFilename(name) {
	return name.replace(/[^a-zA-Z1-9]/g,'')+'.txt';
}

//helper
function cutDisplayOrder(aliases, formats, arr) {
	var keyorder = [];
	for (var i=0; i<arr.length; i+=2) {
		var k = arr[i], dk = arr[i+1];
		if ((!dk) || typeof(k) != 'string' || typeof(dk) != 'string')
			throw 'Cockup: misformed displayorder near key: '+arr[i];
		
		keyorder.push(k);
		
		if (aliases[k])
			throw 'Cockup: duplicate displayorder key: '+arr[i];
		aliases[k] = dk;
		
		var fm = arr[i+2];
		if (fm && typeof(fm) != 'string') {
			formats[k] = fm;
			i++;
		}		
	}
	return keyorder;
}
	

	
//private scope
(function(){
	var aliases = {};
	var formats = {};
	var displayorder = cutDisplayOrder(aliases, formats,
	[
	//	dbase key	displayed key		function/dict to format value
		'boosters',	'magic bonus',		function(v){ return boosterFormatter(0,0,v); },
		'ptr',		'magic penetration',
	
		'ap',		'bonus action points?',
		'gA',		'generates fire gems',		formatPerTurn,
		'gB',		'generates blood slaves',	formatPerTurn,
		'gD',		'generates death gems',		formatPerTurn,
		'gE',		'generates earth gems',		formatPerTurn,
		'gF',		'generates fire gems',		formatPerTurn,
		'gS',		'generates astral gems',	formatPerTurn,
		'gN',		'generates nature gems',	formatPerTurn,	
		'gW',		'generates water gems',		formatPerTurn,
		
		'str',		'strength',		formatSigned,
		'inv',		'reinvigoration',
		'att',		'attack',		formatSigned,
				
		'prot',		'protection, body',
		'prothead',	'protection, head',
		'protshield',	'protection, shield',
		'def',		'defence',		formatSigned,
		'parry',	'parry',
		'enc',		'encumbrance',
		
		'airshld',	'air shield',		formatPercent,
		'mr',		'magic resistance',	formatSigned,
		'regen',	'regeneration',		formatPercent,
		
		
		'protm',	'invulnerability', 	function(v){ return v+' protection'; },
		'respsn',	'resist poison',	formatSignedPercent,
	
		'barkskin',	'barkskin',		{1: '10 protection (+1 if already over 10)'},
		'resfire',	'resist fire', 		formatSignedPercent,
	
		'stoneskin',	'stoneskin',		{1: '15 protection (+2 if already over 15)'},
		'rescold',	'resist cold', 		formatSignedPercent,
		
		'resshck',	'resist shock',		formatSignedPercent,
		
		'gemcost',	'gem cost',
		'mor',		'morale bonus',		formatSigned,
		'exp',		'experience bonus',
		'sage',		'research bonus',	formatSigned,
		'prc',		'precision bonus',	formatSigned,
		
		'fshld',	'fire shield',
		'chill',	'chill aura',
		
		'sumrit',		'ritual summons',
		'n_sumrit',		'summon amount',
		'sumbat',		'battle summons',
		'n_sumbat',		'summon amount',
		'sumauto',		'auto battle summons',
		'n_sumauto',		'summon amount',
		'ivylord',		'ivy lord',	function(v){ return '+'+v+' vine man, vine ogre awakening'; },
		'corpselord',		'corpse lord',	function(v){ return '+'+v+' corpse man construction'; },
		
		'onebattlespell',	'casts each battle',
		'overlandspell',	'overland spell',
		'itemspell',		'battle spell',
		'autocombatspell',	'casts every round',
		
		'ldr_n',		'leadership',		formatSigned,
		'ldr_m',		'leadership (magic)',	formatSigned,
		'ldr_u',		'leadership (undead)',	formatSigned,
		
		'flytr',	'map army flight',	function(v){ if (v==1) return '(self only)'; else return 'self + total size '+v; },
		'uwtr',		'water breathing',	function(v){ if (v==1) return '(self only)'; else return 'self + total size '+v; },
		'map',		'map flight',		function(v){ if (v==1) return '(self only)'; else return 'self + total size '+v; },
		
		'insa',		'grows insane',		function(v){ return '+'+v+'% chance per turn'; },
		'horror',	'horror mark',		function(v){ return v+'% chance'; },
		'brsrk',	'berserk when wounded',
		'abrsrk',	'gone berserk',	{0:'+0'},
		'awe',		'awe',
		'aawe',		'animal awe',
		
		'std',		'battle standard (morale)',
		
		'ptrl',		'patrol bonus',		formatSigned,
		'bhunt',	'blood hunt bonus',	formatSigned,
		'sup',		'supply bonus',		formatSigned,
		'sge',		'siege bonus',		formatSigned,
		'forge',	'forge bonus',		formatPercent,
		'pllg',		'pillage bonus',	formatSigned,
		
		'cannotwear',	'restriction',	{2:'non mounted'}
	]);
	var flagorder = cutDisplayOrder(aliases, formats,
	[
	//	dbase key	displayed key		function/dict to format value
		'eth',		'ethereal',
		'nodrop',	'unremovable',
		'bless',	'blessed',
		'trmpl',	'trample',
		'fly',		'flying',
		'quick',	'quickness',
		'bldvng',	'blood vengeance',
		'disease',	'diseased',
		'airbr',	'air breathing',
		'luck',		'lucky',
		'curse',	'cursed',
		'nopickup',	'won\'t be picked up',
		'haste',	'double action points',
		'nodiscount',	'no forge discounts'	
	]);
	var hiddenkeys = {
		id:1,
		name:1,
		mpath:1, 
		type:1, 
		wpn:1, 
		armor:1, 
		con:1, 
		p1:1, lv1:1, p2:1, lv2:1, 
		A:1, B:1, D:1, E:1, F:1, N:1, S:1, W:1, H:1,
		descr:1,
		gemcost:1,
		
		tablei:1,
		wpnclass:1,
		searchable:1,
		notes:1,
		originalname:1,
		copyspr:1
	};		
		
	var formatItemType = {	'2-h wpn':'two handed weapon', 
				'1-h wpn':'one handed weapon', 
				'misc':'miscellaneous',
				'helm':'helmet',
				'shield':'shield',
				'armor':'armor',
				'boots':'boots'
			};
	var formatItemCon = {	0:'(lvl 0)',
				2:'(lvl 2)',
				4:'(lvl 4)',
				6:'(lvl 6)',
				8:'(lvl 8 - unique)',
				12:''
			};
	
		
	ItemGrid.prototype.renderOverlay = function(o) {
		var descrpath = 'gamedata/itemdescr/';
		
		//template
		var h=''
		h+='<div class="item-details"> ';
		
		//header
		h+='	<div class="item-header" title="item id: '+o.id+'""> ';
		h+='		<div class="item-image" style="background-image:url(\'images/items/item'+o.copyspr+'.png\');">&nbsp;</div> ';
		h+='		<h2 class="item-name">'+o.name+'</h2> ';
		h+='		<p class="item-type">'+formatItemType[o.type]+' '+formatItemCon[o.con]+'</p>';
		h+='	</div>';
		
		
		//mid
		h+='	<div class="item-main">';
		h+=' 		<input class="overlay-pin" type="image" src="images/PinPageTrns.png" title="unpin" />';
		h+='		<table class="item-table"> ';
		h+='		<tr class="dom3id"><th>item id:</th><td>'+o.id+'</td></th>';	
		if (o.armor)
			h+='	<tr class="dom3id"><th>armor id:</th><td>'+o.armor.id+' ('+o.armor.armorname+')</td></th>';	
		
		//populate lineorder (ensures no property is missed)
		for (var k in o) {
			if (!hiddenkeys[k] && !aliases[k]) {
				displayorder.push(k);
				aliases[k] = k;
			}
		}
		//table lines
		for (var i=0; i<displayorder.length; i++) {
			var k = displayorder[i];
			var v = o[k];
			if ((!v) || hiddenkeys[k]) continue;
			
			if (formats[k] && typeof(formats[k])=='function')
				v = formats[k](v);
			else if ((k in formats) && formats[k][v])
				v = formats[k][v];
			
			var ak = aliases[k];
			if (d3mi.options['Show database keys'])
				ak+= ' ['+displayorder[i]+']';
			
			if (v!='0')
				h+=' <tr class="'+k+'"> <th>'+ak+': </th> <td>'+v+'</td> </tr> ';	
		}
		//table flags
		var slist = [];
		for (var i=0;  i<flagorder.length; i++) {
			var k = flagorder[i];
			if (o[k]) {
				var ak = aliases[k];
				if (d3mi.options['Show database keys'])
					ak += ' ['+flagorder[i]+']';
				slist.push('<span class="flag">'+ak+'</span>');    
			}
		}
		if (slist.length)
			h+= ' <tr><td colspan="2"> (' + slist.join(', ') + ') </td><tr> ';	
		h+=' </table> ';
		
		//weapon
		if (o.wpn && modctx.wpnlookup[o.wpn]) {
			var isImplicitWpn = (o.type == '1-h wpn' || o.type == '2-h wpn'); 
			h+=renderWpnDetails(modctx.wpnlookup[o.wpn], isImplicitWpn);
		} 
		h+='	</div>';
		
		//footer
		h+='	<div class="item-footer">';
		
		//wikilink
		h+=' <div class="item-wiki-link"><a href="http://dom3.servegame.com/wiki/'+o.name.replace(/ /g, '_')+'">[wiki]</a></div>';
		
		//cost
		if (o.con == '12')
			h+='	<p class="item-gemcost">Item cannot be forged.</p>';
		else
			h+='	<p class="item-gemcost">Requires '+gemFormatter(0,0,o.gemcost) +' to forge ('+pathFormatter(0,0,o.mpath)+')</p>';
		
		var descr = o.descr || '.';
		h+='	<div class="item-descr item-id-'+o.id+'">'+descr+'</div>';
		
		//ajax load descr from file
		if (!o.descr) {
			//animate while loading 
			var hcb = setInterval(function(e){
				$desc = $('.item-descr.item-id-'+o.id);
				if ($desc.length) {
					var str = '&nbsp;&nbsp;&nbsp;'+$desc.html();
					if (str.length > 100) str = '.'
					$desc.html(str);
				}
				else clearInterval(hcb); //overlay closed
			}, 200);
			
			//ajax call to get descr
			$.get(descrpath+descrFilename(o.originalname), function(txt) {
				clearInterval(hcb);
				
				o.descr = txt;
				$('.item-descr.item-id-'+o.id).html(txt);
			}, 'text');
		}
		h+='	</div> ';
		
		h+='</div> ';
		return h;	
	}

}())
//end private scope


function renderWpnDetails(o, isImplicitWpn) {
	if (!o.dmg) o.dmg = 0;
	
	//these rows will come first (if valid) then any remaining keys
	var lineorder = [
		'dmg',
		'rng',
		'att',
		'def',
		'lgt',
		'#att',
		'shots',
		'aoe'	
	].slice();
	//keys to rename
	var alias = {
		att:   'attack',
		def:   'defence',
		lgt:   'length',
		'#att': 'nbr of attacks',
		dmg:   'damage',
		aoe: 'area effect',
		shots: 'ammunition',
		rng: 'range'
	};
	//boolean flags (listed on one line without values)
	var flags = {		
		dt_stun: 'stun damage only',
		dt_paralyze: 'paralyze damage only',
		dt_poison: 'poison damage only',
		dt_cap: 'max 1hp damage',
		dt_demon: 'x2 vs demons',
		dt_demononly: 'only damages demons',
		dt_holy: 'x3 vs undead/demon',
		dt_magic: 'x2 vs magic beings',
		dt_magiconly: 'only damages magic beings',
		dt_small: 'x2 vs smaller beings',
		dt_large: 'x2 vs larger beings',
		dt_constr: 'only damages lifeless beings',
		dt_raise: 'victims raised as soulless',
		mind: 'mindless beings are immune',
		cold: 'cold damage',
		fire: 'fire damage',
		shock: 'shock damage',
		poison: 'poison',
		
		partdrain: 'partial life drain',
		drain: 'life drain',
		
		charge:	'charge bonus',
		flail:	'+2 att vs shields',
		mres:	'MR negates',
		mreseasy:	'MR negates easily'
	};
	//listed next to damage
	var dmgflags = {
		ap: 'armor piercing',
		an: 'armor negating',
		dt_cap: 'max 1 HP damage',
		nostr: 'no strength',
		magic: 'magic'
	}	
	//skip these altogether
	var skipkeys = {
		id:1,
		wpname:1,
		dt_normal: 'damage type normal',
		bonus: 'no hands needed?',
		//nostr:1,
		//dt_cap:1,
		effect:1,
		effectauto:1,
		res: 'resource cost',
		'2h': '2 handed'
	};
	//convert values
	var convert = {
		'#att': function(v){ if (v=='1' && isImplicitWpn) return '0'; return v; },
		dmg: function(v) {
			var slist = [];
			for (var k in dmgflags)
				if (o[k] == '1') slist.push('<span class="flag">'+dmgflags[k]+'</span>');
			if (slist.length)
				v += ' (' + slist.join(', ') + ')';
			return v;
		}
	};
	//ranged weapon specifics
	if (o.shots) {
		alias.att = 'precision';
		convert['#att'] = function(v) {
			if (v<2) return { '-2':'1 per 2 turns', '-3':'1 per 3 turns' }[v];
			else return v;
		};
		convert.rng = { 1:'0', '-1':'strength', '-2':'strength/2', '-3':'strength/3', '-4':'strength/4', '-5':'strength/5'};
		convert.shots =  {'0':'12'};
	}

	
	//template
	var h=' <table class="wpn-table"> ';
	h+='	<tr class="dom3id"><th>weap id:</th><td>'+o.id+' ('+o.wpname+')</td></th>';	

	//populate lineorder
	for (var k in o) {
		if (!flags[k] && !dmgflags[k] && !skipkeys[k] && !inArray(k, lineorder))
			lineorder.push(k);
	}
	//render lines
	for (var i=0; i<lineorder.length; i++) {
		var k = lineorder[i]; 
		var v = o[k];
		if (convert[k] && typeof(convert[k])=='function')
			v = convert[k](v);
		else if ((k in convert) && convert[k][v])
			v = convert[k][v];			
		if (alias[k])
			k = alias[k];
		if (v && v!='0')
			h+=' <tr> <th>'+k+': </th> <td>'+v+'</td> </tr> ';	
	}
	//render flags
	h+=' <tr><td colspan="2"> ';
	var slist = [];
	for (var k in flags) {
		if (o[k]) {
			slist.push('<span class="flag">'+flags[k]+'</span>');
		}
	}
	if (slist.length)
		h+= ' <tr><td colspan="2"> (' + slist.join(', ') + ') </td><tr> ';	
	h+=' </table> ';
	
	//effects are implemented as further weapons
	var effect = modctx.wpnlookup[o.effect];
	var effectauto = modctx.wpnlookup[o.effectauto];
	
	if (o.effectauto && effectauto) {
		h+=' <h4>Auto effect: '+effectauto.wpname+'</h4>';
		if (effectauto == o)
			alert('Error, weapon 2nd effect as itself: '+o.id+': '+o.wpname); 
		else {
			h+=renderWpnDetails(effectauto, true);
		}
	} 
	else if (o.effect && effect) {
		h+=' <h4>On-hit effect: '+effect.wpname+'</h4>';
		if (effect == o)
			alert('Error, weapon 2nd effect as itself: '+o.id+': '+o.wpname); 
		else {
			h+=renderWpnDetails(effect, true);
		}
	}
	return h;
}


